/* $Id: atable.c,v 1.2 2006-10-23 17:13:52 nicolasw Exp $ */

/* Tabla hash para direcciones de memoria. Escrito por Rafael Carrascosa para la catedra de
 * Sistemas Operativos, FaMAF, UNC.
 * Eres libre de modificar,utilizar y distribuir este codigo para cualquier 
 * fin siempre y cuando mantengas la autoria.
 */

#include <assert.h>
#include <malloc.h>
#include <string.h>
#include "atable.h"

#define dom_t unsigned short
/* Notar que: 1<<x es equivalente a 2^x */
#define RANGE 1<<(8*sizeof(dom_t))
#define INVREP(x) (x != NULL && x->atable != NULL) 

typedef struct slist_t *list_t;

struct slist_t
{
	void *key;
	size_t value;
	list_t next;
};

struct atable_s
{
	list_t *atable;
};

/*    En honor a los miticos Deep Purple */
static dom_t hush(void *key)
{
	dom_t rta=0;
	size_t i=0;
	while(i != sizeof(void *) / sizeof(dom_t))
	{
		/* El casting es para hacer un arreglo con elementos */
		/* de tamanio sizeof(dom_t)*/
		rta ^= ((dom_t *) &key) [i];
		i++;
	}
	return rta;
}

atable_s *atable_create (void)
{
	atable_s *t;
	t = malloc(sizeof(atable_s));
	if(t == NULL) return t;
	t->atable = calloc(RANGE,sizeof(char *));
	if(t->atable == NULL)
	{
		free(t);
		return NULL;
	}
	return t;
}

int atable_insert (atable_s *t,void *key,size_t value)
{
	dom_t index;
	list_t node = NULL;
	assert(INVREP(t) && !atable_isdef(t,key));
	node = malloc(sizeof(struct slist_t));
	if(node == NULL) return 1;

	index = hush(key);
	node->key = key;
	node->value = value;
	node->next = t->atable[index];
	t->atable[index] = node;
	return 0;
}

size_t atable_get (const atable_s *t,void *key)
{
	list_t finder=NULL;
	dom_t index;
	size_t value=0;
	assert(INVREP(t) && atable_isdef(t,key));

	index = hush(key);
	finder = t->atable[index];
	/* Notar el pesadisimo uso de las postcondiciones de isdef */
	while(key != finder->key) finder = finder->next;
	value = finder->value;
	finder = NULL;
	return value;
}

int atable_isdef (const atable_s *t,void *key)
{
	list_t finder=NULL;
	dom_t index=0;
	assert(INVREP(t));
	index = hush(key);
	finder = t->atable[index];
	while(finder != NULL && key != finder->key)
		finder = finder->next;
	return finder != NULL;
}

void atable_remove (atable_s *t,void *key)
{
	list_t finder=NULL,last=NULL;
	dom_t index;
	assert(INVREP(t) && atable_isdef(t,key));
	index = hush(key);
	finder = t->atable[index];
	while(key != finder->key)
	{
		last=finder;
		finder = finder->next;
	}

	if(last == NULL)
		t->atable[index] = finder->next;
	else
		last->next = finder->next;
	free(finder);
	finder = NULL;
}

void atable_destroy(atable_s *t)
{
	list_t finder=NULL,last=NULL;
	size_t i=0;
	assert(INVREP(t));
	for(i=0;i!= RANGE;i++)
	{
		finder = t->atable[i];
		while(finder != NULL)
		{
			last=finder;
			finder=finder->next;
			free(last);
		}
	}
	free(t->atable);
	free(t);
	return;
}
